#include <chrono>
#include "NodeHost.h"

#include "PCA9535.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/select.h>

#include "cJSON.h"
#include "wiringPi.h"
#include "wiringPiI2C.h"
#include "wiringSerial.h"


NodeAppBoard::NodeAppBoard()
{
}
NodeAppBoard::~NodeAppBoard()
{
}
bool NodeAppBoard::used() const 
{
		return mUsed;
}
int NodeAppBoard::addr() const 
{
		return mAddr;
}
string NodeAppBoard::model() const
{
		return mModel;
}
string NodeAppBoard::appid() const 
{
		return mAppID;
}
void NodeAppBoard::setBoard(bool use)
{
		mUsed = use;
}

void NodeAppBoard::setBoard(int addr,string model,string id)
{
		mUsed = true;
		mAddr = addr;
		mModel = model;
		mAppID = id;
}

/*===================================================*/

#ifdef HOST_ORANGE_PI
const char * uart_dev[UART_MAX_NUM] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3"};
#else
const char * uart_dev[UART_MAX_NUM] = {"/dev/ttyAMA0"};
#endif

NodeHost * NodeHost::mHost = nullptr;
NodeHost* NodeHost::getHost()
{
		if(mHost==nullptr)
		{
				mHost = new NodeHost("RaspPi");
		}
		return mHost;
}
NodeHost::NodeHost()
{
		mName = "RaspPi";
		mAppFd = -1;

		for(int i=0;i<APPBOARD_MAX_NUM;i++)
		{
				mBoards[i].setBoard(false);
		}
		mTemprature = 0.0;
		mHumidity = 0.0;
		mAirpress = 0.0;
		mLuminosity = 0.0;

}
NodeHost::~NodeHost()
{
		mThread[0].join();
		mThread[1].join();
		mThread[2].join();
		if(mAppFd > 0)
		{
				close(mAppFd);
				mAppFd = -1;
		}
		if(m485Fd > 0)
		{
				serialClose(m485Fd);
				m485Fd = -1;
		}
}
NodeHost::NodeHost(string name):mName(name)
{
		mAppFd = -1;
		m485Fd = -1;
}
bool NodeHost::initEnv()
{
		printf("start wiringpi..\n");
		wiringPiSetup();
		printf("end wiringpi settup!\n");

		pinMode(PIN_PHYKEY_IO_INT,INPUT);
		pinMode(PIN_BOARD_CHN_INT,INPUT);

		/******** 485 Settings **********/
		pinMode(PIN_UART_485D_CTRL,OUTPUT);
		digitalWrite(PIN_UART_485D_CTRL,HIGH);//485 output

        m485Fd = serialOpen(uart_dev[0],UART_BAUDRATE);
		std::cout << "open serial: " << uart_dev[0] << ",baudrate:" << UART_BAUDRATE << ",fd = " << m485Fd << std::endl;

		/******** init pca9335 expand io ********/
		std::cout << "init PCA9535 io-ext: 0x20" << std::endl;
		mIOLedKey.setAddr(0x20);
		mIOLedKey.begin();
		mIOLedKey.setConfigPort(0,0x00); // all out
		mIOLedKey.setConfigPort(1,0xFE); // P1_0 - out
		mIOLedKey.setOutPort(0,0x00);// default level
		mIOLedKey.setOutPort(1,0x00);

		std::cout << "init PCA9535 io-ext: 0x21" << std::endl;
		mIOChnInt.setAddr(0x21);
		mIOChnInt.begin();
		mIOChnInt.setConfigPort(0,0xFF);
		mIOChnInt.setConfigPort(1,0xF0);
		mIOChnInt.setOutPort(1,0x00);

		std::cout << "init PCA9535 io-ext: 0x22" << std::endl;
		mIOChnDis.setAddr(0x22);
		mIOChnDis.begin();
		mIOChnDis.setConfigPort(0,0x00);
		mIOChnDis.setConfigPort(1,0x00);
		mIOChnDis.setOutPort(0,0x00);
		mIOChnDis.setOutPort(1,0x00);

		return true;
}
void NodeHost::startService()
{
		mThread[0] = std::thread(&NodeHost::detectAppBoard,this);
		mThread[1] = std::thread(&NodeHost::recvAppCmd,this);
		mThread[2] = std::thread(&NodeHost::sendAppMsg,this);
}

float NodeHost::readTemprature()
{
		float temp;
		if(SimpleDHTErrSuccess!=mTempHumi.read2(PIN_SENSOR_DHT11,&temp,nullptr,nullptr))
		{
				return mTemprature;
		}	
		mTemprature = temp;
		return temp;
}
float NodeHost::readHumidity()
{
		float humi;
		if(SimpleDHTErrSuccess!=mTempHumi.read2(PIN_SENSOR_DHT11,nullptr,&humi,nullptr))
		{
				return mHumidity;
		}	
		mHumidity = humi;
		return humi;
}
float NodeHost::readAirpressure()
{
		return mAirpress;
}

float NodeHost::readLuminosity()
{
		return mLuminosity;
}

DateTime NodeHost::getTime()
{
		return mRtc.readTime();
}

void NodeHost::setTime(DateTime& dt)
{
		mRtc.setTime(dt);
}

int NodeHost::getAppBoard(NodeAppBoard boards[])
{
		int num = 0;
		for(int i = 0;i<APPBOARD_MAX_NUM;i++)
		{
				if(mBoards[i].used())
				{
						boards[num++] = mBoards[i];
				}
		}
		return num;
}
bool NodeHost::findAppBoard(int addr)
{
		for(int i = 0;i<APPBOARD_MAX_NUM;i++)
		{
				if(mBoards[i].used() && addr==mBoards[i].addr())
				{
						return true;
				}
		}
		return false;
}
void NodeHost::sendMsgToBoard(int addr,string msg)
{
		for(int i = 0;i<APPBOARD_MAX_NUM;i++)
		{
				if(mBoards[i].used() && addr==mBoards[i].addr())
				{
						mToNanoQueue.push(msg);
						return ;
				}
		}
}
bool NodeHost::readMsgFromBoard(string& msg)
{
		if(!mFromNanoQueue.empty())
		{
				msg = mFromNanoQueue.front() + "\n";
				mFromNanoQueue.pop();
				return true;
		}
		return false;
}
bool NodeHost::eventFromBoard(string& msg)
{
		if(!mSndMsgQueue.empty())
		{
				msg = mSndMsgQueue.front() + "\n";
				mSndMsgQueue.pop();
				return true;
		}
		return false;
}

void NodeHost::recvAppCmd()
{
		std::cout << "start service: recvAppCmd..." << std::endl;

		int ret = -1;
		int sfd = socket(AF_INET,SOCK_STREAM,0);
		if(sfd < 0)
		{
				printf("socket err:%s\n",strerror(errno));
				exit(EXIT_FAILURE);
		}
		printf("serv fd = %d\n",sfd);

		struct sockaddr_in servaddr;
		servaddr.sin_family = AF_INET;
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		servaddr.sin_port = htons(SERV_PORT);

		ret = bind(sfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
		if(ret < 0)
		{
				printf("bind err:%s\n",strerror(errno));
				close(sfd);
				exit(EXIT_FAILURE);
		}

		int reuse = 1;
		setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,(char*)&reuse,sizeof(int));

		struct timeval tout = {3,0};
		setsockopt(sfd,SOL_SOCKET,SO_RCVTIMEO,(char*)&tout,sizeof(tout));
		setsockopt(sfd,SOL_SOCKET,SO_SNDTIMEO,(char*)&tout,sizeof(tout));

		listen(sfd,100);

		struct sockaddr_in cliaddr;
		socklen_t addrlen = sizeof(cliaddr);

		fd_set rdfds;
		fd_set wdfds;

		FD_ZERO(&rdfds);
		FD_ZERO(&wdfds);

		int maxfd = sfd;
		char sndbuffer[1024]={0};
		char rcvbuffer[1024]={0};
		int rcvlen = 0,sndlen = 0;

		printf("start recvmsg...\n");
		while(true)
		{
				FD_ZERO(&rdfds);
				FD_SET(sfd,&rdfds);
				if(mAppFd > 0) FD_SET(mAppFd ,&rdfds);

				tout.tv_sec = 3;
				tout.tv_usec = 0;
				ret = select(maxfd+1,&rdfds,NULL,NULL,&tout);
				if(ret <= 0)
				{
				}
				else
				{
						if(FD_ISSET(sfd,&rdfds))
						{
								if(mAppFd > 0) close(mAppFd);// only one client permission
								mAppFd = accept(sfd,(struct sockaddr*)&cliaddr,&addrlen);
								if(mAppFd > 0)
								{
										printf("client %d addr:%s\n",mAppFd ,inet_ntoa(cliaddr.sin_addr));
										maxfd = (maxfd > mAppFd )?maxfd:mAppFd ;
								}
						}
						else if(FD_ISSET(mAppFd,&rdfds))
						{
								memset(rcvbuffer,0,sizeof(rcvbuffer));
								memset(sndbuffer,0,sizeof(sndbuffer));

								rcvlen = recv(mAppFd,rcvbuffer,sizeof(rcvbuffer),0);
								if(rcvlen <= 0)
								{
										printf("recv ret = %d\n",rcvlen);
										close(mAppFd);
										mAppFd = -1;
								}
								else
								{
										printf("recv data:%s\n",rcvbuffer);
										// process buffer
										sndlen = process_app_request(rcvbuffer,rcvlen,sndbuffer,sizeof(sndbuffer));
										printf("process request sndlen = %d\n",sndlen);
#if 1
										send(mAppFd,sndbuffer,sndlen,MSG_NOSIGNAL);
#else
										if(sndlen > 0)
										{
												string sndMsg(sndbuffer);
												mSndMsgQueue.push(sndMsg);
										}
#endif
								}
						}
				}
		}
		close(sfd);

}

void NodeHost::sendAppMsg()
{
		int ret = 0;

		printf("start service: sendAppMsg...\n");
		while(true)
		{
				if(mAppFd > 0 && !mSndMsgQueue.empty())
				{
						const char *buf = mSndMsgQueue.front().c_str();
						int sndlen = mSndMsgQueue.front().length();

						ret = send(mAppFd,buf,sndlen,MSG_NOSIGNAL);
						printf("send ret = %d\n",ret);
						if(ret < 0)
						{
								close(mAppFd);
								mAppFd = -1;  
						}
						mSndMsgQueue.pop();
				}	
				if(mSndMsgQueue.empty() || mAppFd < 0)
				{
					std::this_thread::sleep_for(std::chrono::milliseconds(10));
				}
		}
}

void NodeHost::detectAppBoard()
{
		int chn[16]={HIGH}; // default 
		int val0 = 0,val1 = 0;
		bool pulse[16]={false};
		std::chrono::high_resolution_clock::time_point tp[16];

		for(auto i=0;i<16;i++) { chn[i] = HIGH; pulse[i] = false;}
		std::cout << "start service: detectAppBoard ..." << std::endl;
		val0 = mIOChnInt.readInPort(0);
		val1 = mIOChnInt.readInPort(1); val1 = (val1>>4)&0x0f;
		std::cout << "Port 0: " << val0 << ",Port 1:" << val1 << std::endl;
		while(true)
		{
				if(!mToNanoQueue.empty())
				{
						string tomsg = mToNanoQueue.front();
						std::cout << "$$ cmd to nano:" << tomsg << std::endl;
						write485(tomsg.c_str());
						mToNanoQueue.pop();

						string frommsg;
						if(read485(frommsg)>0)
						{
							//mFromNanoQueue.push(frommsg);
							mSndMsgQueue.push(frommsg);
							std::cout << "$$ ack from nano: " << frommsg << std::endl;
						}
				}

				val0 = mIOChnInt.readInPort(0);
				val1 = mIOChnInt.readInPort(1); val1 = (val1>>4)&0x0f;
				//if(digitalRead(PIN_BOARD_CHN_INT)==0) // has interrupt
				{
						//val0 = mIOChnInt.readInPort(0);
						//val1 = mIOChnInt.readInPort(1); val1 = (val1>>4)&0x0f;

						for(int i=0;i<APPBOARD_MAX_NUM;i++)
						{
								if(i<8) {
										if(chn[i]==HIGH && ((val0>>i)&0x01)==0)
										{// Insert AppBoard
												chn[i] = LOW;
												on_insert_board(i);
										}
										else if(chn[i]==LOW && ((val0>>i)&0x01)==0)
										{
												if(pulse[i])
												{
														on_notify_board(i);
														pulse[i] = false;
												}
										}
										else if(chn[i]==LOW && ((val0>>i)&0x01)==1)
										{// Remove or Notify
												if(!pulse[i])
												{
														pulse[i] = true;
														tp[i] = std::chrono::high_resolution_clock::now();
												}

												if(std::chrono::high_resolution_clock::now() - tp[i] > std::chrono::milliseconds(20))
												{
														chn[i] = HIGH;
														pulse[i] = false;

														on_remove_board(i);
												}
										}
								}
								else 
								{
										if(chn[i]==HIGH && ((val1>>(i-8))&0x01)==0)
										{// Insert AppBoard
												chn[i] = LOW;
												on_insert_board(i);
										}
										else if(chn[i]==LOW && ((val1>>(i-8))&0x01)==0)
										{
												if(pulse[i])
												{
														on_notify_board(i);
														pulse[i] = false;
												}
										}
										else if(chn[i]==LOW && ((val1>>(i-8))&0x01)==1)
										{
												if(!pulse[i])
												{
														pulse[i] = true;
														tp[i] = std::chrono::high_resolution_clock::now();
												}

												if(std::chrono::high_resolution_clock::now() - tp[i] >= std::chrono::milliseconds(20))
												{
														chn[i] = HIGH;
														pulse[i] = false;

														on_remove_board(i);
												}
										}
								}
						}
				}
				std::this_thread::sleep_for(std::chrono::microseconds(20));// 485 half-duplex sync need!
		}
}

int NodeHost::process_app_request(char *in,int len,char *out,int outlen)
{
    if(in == NULL ||out==NULL|| len <=0)
        return -1;
       
    int sndlen = 0;
    cJSON *inmsg = cJSON_Parse(in);
    if(inmsg == NULL)
    {
        printf("json parse fail!\n");
        return -1;
    }
    cJSON *outmsg = cJSON_CreateObject();
    //parse and act
    cJSON *gpio = cJSON_GetObjectItem(inmsg,"gpio");
    cJSON *i2c = cJSON_GetObjectItem(inmsg,"i2c");
    cJSON *uart = cJSON_GetObjectItem(inmsg,"uart");
    
    cJSON *item = NULL;
    if(gpio)
    {
        int res = 0;
        cJSON_AddItemToObject(outmsg,"gpio",item = cJSON_CreateObject());
            
        cJSON *cmd = cJSON_GetObjectItem(gpio,"cmd");
        cJSON *pins = cJSON_GetObjectItem(gpio,"pins");
        int num = cJSON_GetArraySize(pins);
        
        if(cmd==NULL || pins==NULL || num==0)
        {
            res = RES_CODE_INVALIDCMD;
        }
        else if(strcmp(cmd->valuestring,CMD_WRITE)==0)
        {
            cJSON *vals = cJSON_GetObjectItem(gpio,"vals");
            
            int i = 0;
            for(i=0;i<num;i++)
            {
                int pin = cJSON_GetArrayItem(pins,i)->valueint;
                int val = cJSON_GetArrayItem(vals,i)->valueint;
                
                pinMode(pin,OUTPUT);
                digitalWrite(pin,val);
            }                        
        }
        else if(strcmp(cmd->valuestring,CMD_READ)==0)
        {      
            int i = 0;
            int *pin = (int*)malloc(num*sizeof(int));
            int *val = (int*)malloc(num*sizeof(int));
            for(i=0;i<num;i++)
            {
                pin[i] = cJSON_GetArrayItem(pins,i)->valueint;
                
                pinMode(pin[i],INPUT);
                val[i] = digitalRead(pin[i]);
            }
            cJSON *pinsItem = cJSON_CreateIntArray(pin,num);
            cJSON *valsItem = cJSON_CreateIntArray(val,num);
            cJSON_AddItemToObject(item,"pins",pinsItem);
            cJSON_AddItemToObject(item,"vals",valsItem);
            
            free(pin);free(val);pin = val = NULL;
        }
        else
        {
            res = RES_CODE_INVALIDCMD;
        }
        cJSON_AddNumberToObject(item,"resp",res);
    }
    if(i2c)    
    {
        int res = 0;
        // response
        cJSON_AddItemToObject(outmsg,"i2c",item = cJSON_CreateObject());
    
        cJSON *cmd = cJSON_GetObjectItem(i2c,"cmd");
        cJSON *bus = cJSON_GetObjectItem(i2c,"bus");
        cJSON *addr = cJSON_GetObjectItem(i2c,"addr");
        cJSON *regs = cJSON_GetObjectItem(i2c,"regs");       
        
        if(cmd==NULL || bus==NULL || addr==NULL || regs == NULL)
        {
            res = RES_CODE_INVALIDCMD;
            cJSON_AddNumberToObject(item,"resp",res);
            goto process_end;
        }
        
        int num = cJSON_GetArraySize(regs);
        int fd = wiringPiI2CSetup(addr->valueint);
        printf("i2c setup addr:%02x fd = %d, num = %d\n",addr->valueint,fd,num);
        
        if(num==0 || fd < 0)
        {
            res = RES_CODE_INVALIDCMD;
            cJSON_AddNumberToObject(item,"resp",res);
            goto process_end;
        }
        
        if(strcmp(cmd->valuestring,CMD_WRITE)==0)
        {
            cJSON *vals = cJSON_GetObjectItem(i2c,"vals");
            int i = 0;
            for(i=0;i<num;i++)
            {
                int reg = cJSON_GetArrayItem(regs,i)->valueint;
                int val = cJSON_GetArrayItem(vals,i)->valueint;
                
                wiringPiI2CWriteReg8(fd,reg,val);
            }
        }
        else if(strcmp(cmd->valuestring,CMD_READ)==0)
        {
            cJSON_AddNumberToObject(item,"bus",bus->valueint);
            cJSON_AddNumberToObject(item,"addr",addr->valueint);
            
            int i = 0;
            int *reg = (int*)malloc(num*sizeof(int));
            int *val = (int*)malloc(num*sizeof(int));
            for(i=0;i<num;i++)
            {
                reg[i] = cJSON_GetArrayItem(regs,i)->valueint;
                val[i] = wiringPiI2CReadReg8(fd,reg[i]);
            }
            cJSON *regsItem = cJSON_CreateIntArray(reg,num);
            cJSON *valsItem = cJSON_CreateIntArray(val,num);
            cJSON_AddItemToObject(item,"regs",regsItem);
            cJSON_AddItemToObject(item,"vals",valsItem);
            
            free(reg);free(val);reg = val = NULL;
        }
        else
        {
            res = RES_CODE_INVALIDCMD;
        }
        // response
        cJSON_AddNumberToObject(item,"resp",res);
        close(fd);// close i2c setup
    }
    if(uart)
    {
        int res = 0;
        // response
        cJSON_AddItemToObject(outmsg,"uart",item = cJSON_CreateObject());
        
        cJSON *cmd = cJSON_GetObjectItem(uart,"cmd");
        cJSON *port = cJSON_GetObjectItem(uart,"port");
        //cJSON *len = cJSON_GetObjectItem(uart,"len");
        if(cmd==NULL || port==NULL ||port->valueint<0 || port->valueint>UART_MAX_NUM)
        {
            res = RES_CODE_INVALIDCMD;
            cJSON_AddNumberToObject(item,"resp",res);
            goto process_end;
        }
        
        int fd = serialOpen(uart_dev[port->valueint],UART_BAUDRATE);
        int len = 0;
        printf("serial open %s %d fd = %d\n",uart_dev[port->valueint],UART_BAUDRATE,fd);
        if(fd < 0)
        {
            res = RES_CODE_INVALIDCMD;
            cJSON_AddNumberToObject(item,"resp",res);
            goto process_end;
        }
        
        if(strcmp(cmd->valuestring,CMD_WRITE)==0)
        {
            cJSON *data = cJSON_GetObjectItem(uart,"data");
            cJSON *text = cJSON_GetObjectItem(uart,"text");
            if(data)
            {
                len = cJSON_GetArraySize(data);
                int i = 0;
                for(i=0;i<len;i++) 
                {
                    unsigned char byte = cJSON_GetArrayItem(data,i)->valueint;
                    serialPutchar(fd,byte);
                }
            }
            else if(text)
            {
                len = strlen(text->valuestring);
                printf("text:%s,len = %d\n",text->valuestring,len);
                serialPuts(fd,text->valuestring);
            }
            
            //serialFlush(fd); // DONOT call this to flush!!!
        }
        else if(strcmp(cmd->valuestring,CMD_READ)==0)
        {
            cJSON_AddNumberToObject(item,"port",port->valueint);
            
            int data[1024]={0};
            len = 0;
            while(serialDataAvail(fd)>0)
            {
                data[len++] = serialGetchar(fd);
            }
            cJSON_AddNumberToObject(item,"len",len);
            cJSON_AddItemToObject(item,"data",cJSON_CreateIntArray(data,len));
        }
        else
        {
            res = RES_CODE_INVALIDCMD;
        }
        
        // response
        cJSON_AddNumberToObject(item,"resp",res);
        serialClose(fd);
    }
    
process_end:
    cJSON_free(inmsg);
    if(cJSON_PrintPreallocated(outmsg,out,outlen,0)==1)
    {// success
        sndlen = strlen(out);
    }
    else {sndlen = -1;}
    cJSON_free(outmsg);
    
    return sndlen;
}


void NodeHost::on_insert_board(int chn)
{
		int addr = chn+1;
		char sndmsg[1024];

		std::cout << "on insert board,chn = " << chn << std::endl;

		//Step 1: Config Address
		cJSON *configRoot = cJSON_CreateObject();
		cJSON_AddStringToObject(configRoot,"cmd","host_config_addr");	
		cJSON_AddNumberToObject(configRoot,"dest",APPBOARD_DEF_ADDR);
		cJSON_AddNumberToObject(configRoot,"addr",addr);

		cJSON_PrintPreallocated(configRoot,sndmsg,sizeof(sndmsg),0);
		cJSON_free(configRoot);

		//serialPuts(m485Fd,sndmsg);
		write485(sndmsg);

		//std::this_thread::sleep_for(std::chrono::milliseconds(5));

		std::cout << "after config addr ,wait board ack..." << std::endl;

		//Step 2:Recv Ack 
		char data[1024];
		if(read485(data,sizeof(data)) <= 0)
		{
				std::cout << "read 485 timeout(ms): " << TIMEOUT_485_READ_MS << std::endl;
				return ;
		}

		std::cout << "read 485 data: " << (const char *)data << std::endl;

		cJSON *infoRoot = cJSON_Parse(data);
		if(infoRoot != nullptr)
		{
				cJSON* item_cmd = cJSON_GetObjectItem(infoRoot,"cmd");
				cJSON* item_addr = cJSON_GetObjectItem(infoRoot,"addr");
				cJSON* item_model = cJSON_GetObjectItem(infoRoot,"model");
				cJSON* item_appid = cJSON_GetObjectItem(infoRoot,"appid");
				
				if(item_cmd && item_addr && item_model && item_appid && addr==item_addr->valueint)
				{
						const char *ml = cJSON_GetObjectItem(infoRoot,"model")->valuestring;
						uint32_t id = cJSON_GetObjectItem(infoRoot,"appid")->valueint;

						string model(ml);
						string appid(std::to_string(id));

						//Step 3: Modify Info
						mBoards[chn].setBoard(addr,model,appid);

						//Step 4: Notify Users
						cJSON_ReplaceItemInObject(infoRoot,"cmd",cJSON_CreateString("host_event_insert_board"));
						cJSON_PrintPreallocated(infoRoot,data,sizeof(data),0);

						string appmsg(data);
						mSndMsgQueue.push(appmsg);
				}

				cJSON_free(infoRoot);
		}
}
void NodeHost::on_remove_board(int chn)
{
		int addr = chn+1;
		
		std::cout << "remove board,chn = " << chn << std::endl;

		// Step 1: Modify Info
		mBoards[chn].setBoard(false);	

		// Step 2[sock]: Send Msg To UpperApp
		cJSON *eventRoot = cJSON_CreateObject();
		cJSON_AddStringToObject(eventRoot,"cmd","host_event_remove_board");
		cJSON_AddNumberToObject(eventRoot,"addr",addr);

		char data[1024];
		cJSON_PrintPreallocated(eventRoot,data,sizeof(data),0);

		string appmsg(data);
		mSndMsgQueue.push(appmsg);
}

void NodeHost::on_notify_board(int chn)
{
		int addr = chn+1;
		//using namespace std::chrono;
		//high_resolution_clock::time_point t1 = high_resolution_clock::now();
		//high_resolution_clock::time_point t2 = high_resolution_clock::now();

		//std::cout << "notify board,chn = " << chn << std::endl;

		digitalWrite(PIN_UART_485D_CTRL,HIGH);

		char sndmsg[1024];

		// Step 1[485]: send msg to board to enable notify msg
	    cJSON *enNotifyRoot = cJSON_CreateObject();
		cJSON_AddStringToObject(enNotifyRoot,"cmd","host_enable_notify");	
		cJSON_AddNumberToObject(enNotifyRoot,"dest",addr);
		cJSON_PrintPreallocated(enNotifyRoot,sndmsg,sizeof(sndmsg),0);	
		
		cJSON_free(enNotifyRoot);
		write485(sndmsg);

		// Step 2[485]: recv board msg
		string appmsg;
		if(read485(appmsg) <= 0)
		{
				std::cout << "read 485 timeout! " << std::endl;
				return ;
		}

		std::cout << "read notify from 485: " << appmsg << std::endl;

		//t2 = high_resolution_clock::now();
		//std::cout << "rd took:" << duration_cast<milliseconds>(t2-t1).count() <<" ms" << std::endl;

		if(!appmsg.empty())
		{
				// Step 3[485]: ack board notify msg
				cJSON *ackNotifyRoot = cJSON_CreateObject();
				cJSON_AddStringToObject(ackNotifyRoot,"cmd","host_ack_notify");	
				cJSON_AddNumberToObject(ackNotifyRoot,"dest",addr);
				cJSON_AddNumberToObject(ackNotifyRoot,"resp",0);
				cJSON_PrintPreallocated(ackNotifyRoot,sndmsg,sizeof(sndmsg),0);	

				cJSON_free(ackNotifyRoot);
				write485(sndmsg);

				// Step 4[sock]: send msg to upper app
				mSndMsgQueue.push(appmsg);
		}
}

int NodeHost::write485(const char *msg)
{
		digitalWrite(PIN_UART_485D_CTRL,HIGH);
		std::this_thread::sleep_for(std::chrono::milliseconds(10));

		serialPuts(m485Fd,msg);
		serialPutchar(m485Fd,'\n');
		std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 485 half-commu synchronized need!
		return 0;
}
int NodeHost::read485(string & msg)
{
		std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 485 half-commu synchronized need!
		digitalWrite(PIN_UART_485D_CTRL,LOW); // from out --> in
		
		using namespace std::chrono;
		high_resolution_clock::time_point tp = high_resolution_clock::now();

		char data[1024]={0};
		int len = 0;

		while(high_resolution_clock::now() - tp < milliseconds(TIMEOUT_485_READ_MS))
		{
		if(serialDataAvail(m485Fd)>0)
		{
				//data[len++] = (char)serialGetchar(m485Fd);
				len += read(m485Fd,data+len,sizeof(data)-len);
				if(data[len-1] == '\n') { 
					//std::cout << "inmsg len = " << len << std::endl;
					len -= 1;
					break;
				}
		}
		else std::this_thread::sleep_for(std::chrono::milliseconds(5));
		}
		data[len] = 0;
		digitalWrite(PIN_UART_485D_CTRL,HIGH); 

		msg.assign((const char *)data);
		return len;
}
int NodeHost::read485(char msg[],int size)
{
		std::this_thread::sleep_for(std::chrono::milliseconds(5));
		digitalWrite(PIN_UART_485D_CTRL,LOW); // from out --> in

		using namespace std::chrono;
		high_resolution_clock::time_point tp = high_resolution_clock::now();
		
		int len = 0;
		while(high_resolution_clock::now() - tp < milliseconds(TIMEOUT_485_READ_MS))
		{
		if(serialDataAvail(m485Fd)>0)
		{
				//msg[len++] = (char)serialGetchar(m485Fd);
				len += read(m485Fd,msg+len,size-len);
				if(msg[len-1] == '\n') { len -= 1;break;}
		}
		else std::this_thread::sleep_for(std::chrono::milliseconds(5));
		}
		msg[len] = 0;
		digitalWrite(PIN_UART_485D_CTRL,HIGH); 

		return len;
}

